//
//  SegmentDownloadSession.m
//  SegmentDownloadDemo
//
//  Created by gao on 2018/7/9.
//  Copyright © 2018年 gao. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "SegmentDownloadSession.h"
#import "SegmentDownloadTask.h"
#import "SegmentDownload.h"
@interface SegmentDownloadSession()<NSURLSessionDownloadDelegate>
{
    
}
@property (nonatomic, strong) NSTimer *timer;
@property(nonatomic,strong)NSURLSession *session;
@property(nonatomic,assign)BOOL needReCreateSession;
/**后台下载回调的handlers，所有的下载任务全部结束后调用*/
@property (nonatomic, copy) BGCompletedHandler completedHandler;
@end
@implementation SegmentDownloadSession
+(instancetype)share
{
    static id _shareInstance = nil;
    
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _shareInstance = [[self alloc]init];
    });
    
    
    return _shareInstance;
}

-(instancetype)init{
    if (self = [super init]) {
        self.session = [self getDownloadSession];
//        //获取背景session正在运行的(app闪退会有任务)
//        NSMutableDictionary *dictM = [self.session valueForKey:@"tasks"];
//        [dictM enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, NSURLSessionDownloadTask *obj, BOOL * _Nonnull stop) {
//
//            [SegmentDownload queryTaskWithTaskDescription:obj.taskDescription completion:^(SegmentDownloadTask *task, SegmentDownloader *downloader, SegmentDownloadTask_SubTask *subTask, NSInteger subTaskIndex) {
//
//                    [obj cancel];
//            }];
//
//        }];
    }
    return self;
}

-(NSURLSession *)getDownloadSession{
    NSURLSession *session = nil;
   
//    NSString *identifier = [self backgroundSessionIdentifier];
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
//    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
    sessionConfig.HTTPMaximumConnectionsPerHost = 5;
//    sessionConfig.timeoutIntervalForRequest = 20;
    
    //是否在后台启动app
    sessionConfig.sessionSendsLaunchEvents = YES;
    session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    return session;
}
- (NSString *)backgroundSessionIdentifier {
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    NSString *identifier = [NSString stringWithFormat:@"%@.BackgroundSession", bundleId];
    return identifier;
}
+(NSURLSession *)session{
    SegmentDownloadSession * singleton = [SegmentDownloadSession share];

    return singleton.session;
}
+(void)reCreateSession{
    SegmentDownloadSession * singleton = [SegmentDownloadSession share];
    if (singleton.session != nil) {
        [singleton.session invalidateAndCancel];
        singleton.session = nil;
        [SegmentDownload cleanDownloadSubTaskDownloadTask];
    }
    
    
    

}
+(void)startDownloadSubTask:(SegmentDownloadTask_SubTask *)subTask{

    NSLog(@"start download : %@...",subTask);
    if (subTask.downloadTask == nil) {
        subTask.downloadTask = [subTask getDownloadTask];
    }
    subTask.isDownloading = YES;
    [subTask.downloadTask resume];
    

}


-(void)addCompletionHandler:(BGCompletedHandler)handler identifier:(NSString *)identifier{
    NSLog(@"~~~%s", __func__);
    if ([[self backgroundSessionIdentifier] isEqualToString:identifier]) {
        self.completedHandler = handler;
        //fix a crash in backgroud. for:  reason: backgroundDownload owner pid:252 preventSuspend  preventThrottleDownUI  preventIdleSleep  preventSuspendOnSleep
//        [self startTimer];
        
    }
}
- (void)callBgCompletedHandler {
    NSLog(@"~~~%s", __func__);
    if (self.completedHandler) {
        self.completedHandler();
        self.completedHandler = nil;
    }
}
//将一个后台session作废完成后的回调，用来切换是否允许使用蜂窝煤网络，重新创建session
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error {
    
    self.session = [self getDownloadSession];
    [SegmentDownload startDownload];
}
#pragma mark - NSURLSessionDownloadDelegate
//如果appDelegate实现下面的方法，后台下载完成时，会自动唤醒启动app。如果不实现，那么后台下载完成不唤醒，用户手动启动会调用相关回调方法
//-[AppDelegate application:handleEventsForBackgroundURLSession:completionHandler:]
//后台唤醒调用顺序： appdelegate ——> didFinishDownloadingToURL  ——> URLSessionDidFinishEventsForBackgroundURLSession
//手动启动调用顺序: didFinishDownloadingToURL  ——> URLSessionDidFinishEventsForBackgroundURLSession
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
    NSData * data = [NSData dataWithContentsOfURL:location];

    [SegmentDownload queryTaskWithTaskDescription:downloadTask.taskDescription completion:^(SegmentDownloadTask *task,SegmentDownloader *downloader, SegmentDownloadTask_SubTask *subTask, NSInteger subTaskIndex) {
        if (task && data) {
            subTask.downloadTask = nil;
            NSString *locationString = [location path];
            subTask.tempPath  = locationString;
            [task subTask:subTask downloadFinished:data];
        }
        //URLSessionDidFinishEventsForBackgroundURLSession 方法在后台执行一次，所以在此判断执行completedHandler
        if ([SegmentDownload allTaskFinished]) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"kDownloadAllTaskFinishedNoti" object:nil];
            [self callBgCompletedHandler];
            
        }
        if (downloader) {
            downloader.isWorking = NO;
            downloader.downloadSubTask = nil;
        }
        
    }];
    [SegmentDownload downloaderStartWork];

    
    
        
    
}

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes {
    
    NSLog(@"fileOffset:%lld expectedTotalBytes:%lld",fileOffset,expectedTotalBytes);
}

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    if (session != self.session) {
        return;
    }
    
    [SegmentDownload queryTaskWithTaskDescription:downloadTask.taskDescription completion:^(SegmentDownloadTask *task,SegmentDownloader *downloader, SegmentDownloadTask_SubTask *subTask, NSInteger subTaskIndex) {
    
        if (task.status == kDownloadTaskStatusDownloading) {
            [task subTask:subTask updateDownloadBytes:totalBytesWritten];
        }
        
    }];
    
    
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
    // use response, convert data task to download task
    completionHandler(NSURLSessionResponseBecomeDownload);
    NSLog(@"~~~%s", __func__);
}
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
    NSLog(@"~~~%s", __func__);
}

//后台下载完成后调用。在执行 URLSession:downloadTask:didFinishDownloadingToURL: 之后调用
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    NSLog(@"Background URL session %@ finished events.\n", session);
    NSLog(@"~~~%s", __func__);

}


/*
 * 该方法下载成功和失败都会回调，只是失败的是error是有值的，
 * 在下载失败时，error的userinfo属性可以通过NSURLSessionDownloadTaskResumeData
 * 这个key来取到resumeData(和上面的resumeData是一样的)，再通过resumeData恢复下载
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
    
    if (session != self.session) {
        return;
    }
    
    [SegmentDownload queryTaskWithTaskDescription:task.taskDescription completion:^(SegmentDownloadTask *task,SegmentDownloader *downloader, SegmentDownloadTask_SubTask *subTask, NSInteger subTaskIndex) {
        if (error) {
    
            subTask.downloadTask = nil;
            // check whether resume data are available
            NSData *resumeData = [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData];
            if(resumeData){
                id resumeDataObj = [NSPropertyListSerialization propertyListWithData:resumeData options:0 format:0 error:nil];
                if ([resumeDataObj isKindOfClass:[NSDictionary class]]) {
                    NSDictionary *dic = resumeDataObj;
                    subTask.tmpName = [dic objectForKey:@"NSURLSessionResumeInfoTempFileName"];
                }
            }
//            if (subTask.reDownloadCount < 5) {
                subTask.reDownloadCount ++;
//            }
//            else{

//                [SegmentDownload updateDownloadTask:task status:kDownloadTaskStatusFailed];
                task.faildMessage = [NSString stringWithFormat:@"sub task download count > 5 subtask subindex is %ld errorInfo is %@",subTaskIndex,error.userInfo];
                NSLog(@"ERR");
//            }
        }
        if (downloader) {
            downloader.isWorking = NO;
            downloader.downloadSubTask = nil;
            
        }

        
    }];
    if (error.code == 2 && [error.domain isEqualToString:@"NSPOSIXErrorDomain"]) {
        NSLog(@"Error NSPOSIXErrorDomain");
        [SegmentDownloadSession reCreateSession];
    }
    else{
        [SegmentDownload downloaderStartWork];
    }

    
   
}
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)aSession
              task:(NSURLSessionTask *)aTask
didReceiveChallenge:(NSURLAuthenticationChallenge *)aChallenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))aCompletionHandler
{
    
    NSLog(@"ERR: Missing task description (%@, %d)", [NSString stringWithUTF8String:__FILE__].lastPathComponent, __LINE__);
    aCompletionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}



@end
